//
// This class is used to process the frames of an avi sequence.
// A process(IplImage *im) callback function must be supplied using the 
// setImageProcess() method. This one will be called for each frame
// but you must call callProcess() method before (or dontCallProcess() if
// you do not want the callback to be called).
//

#if !defined AVIP
#define AVIP

#include <process.h>    /* _beginthread, _endthread */
#include "windows.h"      /* Mutex */
#include "highgui.h"  

// typedef struct _tagthreadarglist
// {
// 	void *g;
// 	int *pfreamno;
// 	float *penergy;
// 	float *penergydiff;
// 	float *pmutualinfo;
// 	float *pnmutualinfo;
// }Threadarglist;

unsigned __stdcall processImages( void *);

class AviProcessor 
{

	friend unsigned __stdcall processImages( void *);

	// Object attributes
	void (*process)(IplImage *, float *, float *, float *, float *);
	DWORD delay;
	CvCapture* capture;
	CvVideoWriter* m_pVideoWriter;

	bool display;
	bool call;
	bool stop;
	long fnumber;
	CvFont font;
	CString aviName;
	IplImage *img;
	HANDLE hThread;
	unsigned threadID;
	HANDLE mut;

	void incFrameNumber() { WaitForSingleObject(mut, INFINITE); fnumber++; ReleaseMutex(mut); }

public:
	float *energy;
	float *energydiff;
	float *mutualinfo;
	float *nmutualinfo;
	int *frameno;

public:

	// Constructor
	AviProcessor() : display(false), delay(0), capture(0), fnumber(0), img(0), m_pVideoWriter(0),
		call(true), stop(true), hThread(0)
	{

		mut= CreateMutex(NULL, FALSE, NULL);
		cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1);
	}

	// Destructor
	~AviProcessor() {

		stopIt(); // you should have stopped the capture before
		if (hThread != 0)
			CloseHandle( hThread );

		CloseHandle(mut);

		cvDestroyWindow("Image (AviProcessor)");
		//cvDestroyAllWindows();
		cvReleaseCapture(&capture);
		cvReleaseVideoWriter(&m_pVideoWriter);
	}

	// set the callback function that will be called for each frame
	void setImageProcess(void (*p)(IplImage *, float *, float *, float *, float *)) 
	{

		process= p;
	}

	// stop the capture
	void stopIt()
	{

		WaitForSingleObject(mut, INFINITE);
		stop= true;
		ReleaseMutex(mut);
	}

	bool isStopped()
	{

		bool c;

		WaitForSingleObject(mut, INFINITE);
		c= stop;
		ReleaseMutex(mut);

		return c;
	}

	// the getter and setters to determine if you want the 
	// callback to be called
	void callProcess()
	{

		WaitForSingleObject(mut, INFINITE);
		call= true;
		ReleaseMutex(mut);
	}

	void dontCallProcess() 
	{

		WaitForSingleObject(mut, INFINITE);
		call= false;
		ReleaseMutex(mut);
	}

	bool callingProcess() 
	{

		bool c;

		WaitForSingleObject(mut, INFINITE);
		c= call;
		ReleaseMutex(mut);

		return c;
	}

	// the getter and setter to determine if you want to display 
	// the processed frames
	void setDisplay(bool b)
	{

		WaitForSingleObject(mut, INFINITE);
		if (b)
		{
			cvNamedWindow("Image (AviProcessor)");
			cvMoveWindow("Image (AviProcesor)", 200, 100);
			// 			cvNamedWindow("Result");
			// 			cvMoveWindow("Result", 300+ 400/*image->width*/, 100);
		}
		else
		{
			cvDestroyWindow("Image (AviProcessor)");
			//			cvDestroyWindow("Result");
		}

		display= b;
		ReleaseMutex(mut);
	}

	bool isDisplayed() 
	{

		bool d;

		WaitForSingleObject(mut, INFINITE);
		d= display;
		ReleaseMutex(mut);

		return d;
	}

	// the getter and setter to introduce a delay between each frame
	void setDelay(DWORD d) 
	{

		WaitForSingleObject(mut, INFINITE);
		delay= d;
		ReleaseMutex(mut);
	}

	DWORD getDelay() 
	{

		DWORD d;

		WaitForSingleObject(mut, INFINITE);
		d= delay;
		ReleaseMutex(mut);

		return d;
	}

	// a count is kept of the current frame number
	long getFrameNumber() 
	{

		long f;

		WaitForSingleObject(mut, INFINITE);
		f= fnumber;
		ReleaseMutex(mut);

		return f;
	}

	// size of the frames of the sequence
	CvSize getFrameSize() 
	{

		if (img==0)
			return cvSize(0,0);

		return cvSize(img->width,img->height);
	}

	// set the name of the video file
	void setFile(char *filename) 
	{

		aviName= filename;
		cvReleaseCapture(&capture);
		capture = cvCreateFileCapture(filename);

// 		CvSize img_size;
// 		img_size.height =
// 			(int) cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT );
// 		img_size.width =
// 			(int) cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH );
// 
// 		CvVideoWriter *videoWriter = cvCreateVideoWriter("E:\\Desktop\\abnormal2.avi", CV_FOURCC('X', 'V', 'I', 'D'), 25, img_size, 1);

	}

	void setFile(CString filename) 
	{

		setFile(filename.GetBuffer());
	}

	// to grad (and process) only one frame
	bool grabOneFrame(/*float *penergy, float *penergydiff, float *pmutualinfo, float *pnmutualinfo*/) 
	{

		// make sure that a thread is not already running
		if (!isStopped())
			return false;

		if(cvGrabFrame(capture))
		{

			// gets the current image
			img=cvRetrieveFrame(capture);           

			// calling the process function
			if (callingProcess())
			{
				process(img, energy, energydiff, mutualinfo, nmutualinfo);
				cvWriteFrame(m_pVideoWriter, img);
			}

			if (display)
			{

				// 				CvScalar sc;
				// 				char text[50];
				// 				sprintf(text,"Frame #%6d",fnumber);
				// 
				// 				sc= cvGet2D(img,20,20);
				// 				if (sc.val[1]>128) 
				// 					cvPutText(img, text, cvPoint(20,20), &(font), cvScalar(0,0,0));
				// 				else
				// 					cvPutText(img, text, cvPoint(20,20), &(font), cvScalar(255,255,255));


				cvShowImage( "Image (AviProcessor)", img );
				HWND hWnd= (HWND)cvGetWindowHandle("Image (AviProcessor)");
				::SendMessage(hWnd,WM_PAINT,NULL,NULL); // force the window to repaint
			}

			// increments current frame number
			incFrameNumber();
		}

		return true;
	}

	// to grab (and process) the frame sequence
	bool run(/*Threadarglist *threadarglist*/) 
	{
		
		// make sure that a thread is not already running
		if (!isStopped())
			return false;

		// destroy any previoulsy created thread
		if (hThread != 0)
			CloseHandle( hThread );

		stop= false;

		// start the thread
		CvSize img_size;
		img_size.height =
			(int) cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT );
		img_size.width =
			(int) cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH );

		m_pVideoWriter = cvCreateVideoWriter("E:\\Desktop\\abnormal2.avi", CV_FOURCC('D', 'I', 'V', 'X'), 25, img_size, 1);

		hThread = (HANDLE)_beginthreadex( NULL, 0, 
			processImages,/*reinterpret_cast<LPVOID>(threadarglist)*/reinterpret_cast<LPVOID>(this), 0, &threadID );

		return true;
	}

	// to restart with a new video
	bool restart()
	{

		// make sure that a thread is not already running
		if (!isStopped())
			return false;

		// destroy any previoulsy created thread
		if (hThread != 0)
			CloseHandle( hThread );

		fnumber=0;
		stop= false;
		cvReleaseCapture(&capture);
		capture = cvCaptureFromAVI(aviName.GetBuffer());

		return true;
	}
};

#endif

